#include "web_sock_holder.h"
#include "http_response.h"
#include "http_servlet.h"
#include <stdio.h>

/*

A typical http request header is like this:

GET / HTTP/1.1
Host: localhost:8888
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.1) Gecko/2008072820 Firefox/3.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,* / *;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: UTF-8,*
Keep-Alive: 300
Connection: keep-alive
*/
namespace vmon_web
{
    WebSockHolder::WebSockHolder(int sock, HttpServletContainer& servletContainer):
        STSockHolder(sock), m_state(GETTING_COMMAND), m_response(sock), m_servletContainer(servletContainer)
    {
    }

    WebSockHolder::~WebSockHolder()
    {
    }

    bool WebSockHolder::pushByte(char byte)
    {
        //printf("WebSockHolder::pushByte: function called: state=%d, byte=%c\n", m_state, byte);
        bool ret = true;
        switch (m_state)
        {
            case GETTING_COMMAND:
                if (!isspace(byte))
                {
                    m_line += byte;
                    break;
                }
                else
                {
                    m_request.setCommand(m_line);
                    m_line.clear();
                    ret = gotoState(GETTING_PATH_PRE); /* FALL THROUGH */
                }
            case GETTING_PATH_PRE:
                if (!isspace(byte))
                    ret = gotoState(GETTING_PATH); /* FALL THROUGH */
                else
                    break; /* ignore spaces in this state */
            case GETTING_PATH:
                if (isspace(byte))
                {
                    m_request.setPath(m_line);
                    m_line.clear();
                    ret = gotoState(GETTING_VERSION_PRE);
                    /* FALL THROUGH */
                }
                else
                {
                    m_line += byte;
                    break;
                }
            case GETTING_VERSION_PRE:
                if (isspace(byte))
                {
                    break;
                }
                else
                {
                    ret = gotoState(GETTING_VERSION);
                    /* FALL THROUGH */
                }
            case GETTING_VERSION:
                if (byte == '\r')
                {
                    m_request.setVersion(m_line);
                    m_line.clear();
                    ret = gotoState(GOT_R);
                    break;
                }
                else
                {
                    m_line += byte;
                    break;
                }
            case GETTING_HEADER_KEY:
                if (byte == ':')
                {
                    m_key = m_line;
                    m_line.clear();
                    ret = gotoState(GETTING_HEADER_PRE_VALUE);
                    break;
                }
                else
                {
                    m_line += byte;
                    break;
                }
            case GETTING_HEADER_PRE_VALUE:
                if (isspace(byte))
                {
                    break;
                }
                else
                {
                    ret = gotoState(GETTING_HEADER_VALUE);
                    /* FALL THROUGH */
                }
            case GETTING_HEADER_VALUE:
                if (byte == '\r')
                {
                    m_request.setValue(m_key, m_line);
                    m_key.clear();
                    m_line.clear();
                    ret = gotoState(GOT_R);
                    break;
                }
                else
                {
                    m_line += byte;
                    break;
                }
            case GOT_R:
                if (byte == '\n')
                    ret = gotoState(GOT_N);
                else
                    ret = gotoState(ERROR_STATE);
                break;
            case GOT_N:
                if (byte == '\r')
                    ret = gotoState(GOT_2ND_R);
                else if (!isspace(byte))
                {
                    gotoState(GETTING_HEADER_KEY);
                    ret = pushByte(byte); // recursively call this function
                }
                else
                    ret = gotoState(ERROR_STATE);
                break;
            case GOT_2ND_R:
                if (byte == '\n')
                {
                    // finished getting header
                    if (m_request.getContentLength() > 0)
                        gotoState(GETTING_BODY);
                    else
                        ret = gotoState(DONE);
                }
                else
                    ret = gotoState(ERROR_STATE);
                break;
            case GETTING_BODY:
                if (m_request.getContent().size() >= m_request.getContentLength())
                {
                    ret = gotoState(DONE);
                }
                else
                    m_request.addContentByte(byte);
                break;
            case DONE:
                ret = gotoState(ERROR_STATE);
                break;
            case ERROR_STATE:
                ret = gotoState(ERROR_STATE);
                break;
        }
        return ret;
    }

    bool WebSockHolder::pushEnd()
    {
        printf("---- End of Request ---\n");
        return false;
    }

    bool WebSockHolder::gotoState(State s)
    {
        bool ret = true;
        m_state = s;
        #ifdef DEBUG
        const char* stateNames[] = {
            "GETTING_COMMAND",
            "GETTING_PATH_PRE",
            "GETTING_PATH",
            "GETTING_VERSION_PRE",
            "GETTING_VERSION",
            "GETTING_HEADER_KEY",
            "GETTING_HEADER_PRE_VALUE",
            "GETTING_HEADER_VALUE",
            "GOT_R",
            "GOT_N",
            "GOT_2ND_R",
            "GETTING_BODY",
            "DONE",
            "ERROR_STATE"
        };
        printf("Going to state %s\n", stateNames[s]);
        #endif
        if (s == DONE)
        {
            ret = handleRequest(m_request);
        }
        else if (s == ERROR_STATE)
        {
            send("HTTP/1.1 400 Bad Request\r\n");
            send("Server: vmon_web/0.0\r\n");
            send("Content-Type: text/html; charset=utf-8\r\n");
            send("\r\n");
            send("<html><body>\r\n");
            send("vmon says: I believe your request is not valid\r\n");
            send("</body></html>\r\n");
            ret = false;
        }
        return ret;
    }

// A typical response is like this:
//
//    HTTP/1.1 200 OK
//    Server: nginx/0.5.35
//    Date: Thu, 28 Aug 2008 14:46:24 GMT
//    Content-Type: text/html; charset=gb2312
//    Content-Length: 697
//    Last-Modified: Tue, 06 May 2008 05:16:12 GMT
//    Connection: keep-alive
//    Accept-Ranges: bytes

    bool WebSockHolder::send404(HttpRequest& request)
    {
        // a default handler?
        send("HTTP/1.1 404 Resource Not Found\r\n");
        send("Server: vmon_web/0.0\r\n");
        send("Content-Type: text/html; charset=utf-8\r\n");
        send("\r\n");
        send("<html><body>\r\n");
        send("<strong>Not found Requesting path "+ m_request.getPath() +"</strong><br />\r\n");
        send("<table>");
        map<string, string> keywords = m_request.getKeywords();
        map<string, string>::const_iterator iter = keywords.begin();
        while (iter != keywords.end())
        {
            send(string("<tr><td>") + iter->first + "</td><td>" + iter->second + "</td></tr>");
            ++iter;
        }
        send("</table>");
        send("</body></html>\r\n");
        return false;
    }

    bool WebSockHolder::handleRequest(HttpRequest& request)
    {
        printf("WebSockHolder::handleRequest: handling %s\n", request.getPath().c_str());

        bool rc = m_servletContainer.handleRequest(this);
        if (!m_response.isHandled())
            return send404(m_request);
        // not handled
        return rc;
    }

}
